package top.techqi.core.tool.life

import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Bundle
import android.util.SparseLongArray
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import top.techqi.core.kext.ancestors
import top.techqi.core.kext.poll
import top.techqi.core.util.LogUtil

object LifecycleStat : Application.ActivityLifecycleCallbacks,
    FragmentManager.FragmentLifecycleCallbacks() {
    private const val TAG_BASEMENT = "LifeStat"
    private const val TAG_ACTIVITY = "$TAG_BASEMENT-Activity"
    private const val TAG_FRAGMENT = "$TAG_BASEMENT-Fragment"
    private const val FMT_HASHNAME = "%1$2s(0x%2$08X)"
    private const val FMT_TIMEUSED = "        %1$2s used: %2$3d"

    private val eventsTime = SparseLongArray()

    fun init(app: Application) {
        app.registerActivityLifecycleCallbacks(this)
    }

    override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) {
        eventsTime.put(activity.hashCode(), System.currentTimeMillis())

        if (activity is FragmentActivity) {
            activity.supportFragmentManager.registerFragmentLifecycleCallbacks(this, true)
        }
    }

    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        LogUtil.v(TAG_ACTIVITY, activity.javaClass.simpleName + ": onActivityCreated")
    }

    override fun onActivityPostCreated(activity: Activity, savedInstanceState: Bundle?) {
        val duration = System.currentTimeMillis() - eventsTime.poll(activity.hashCode())
        LogUtil.d(TAG_ACTIVITY, FMT_TIMEUSED.format(activity.javaClass.simpleName, duration))
    }

    override fun onActivityPreStarted(activity: Activity) {
        eventsTime.put(activity.hashCode(), System.currentTimeMillis())
    }

    override fun onActivityStarted(activity: Activity) {
        LogUtil.v(TAG_ACTIVITY, activity.javaClass.simpleName + ": onActivityStarted")
    }

    override fun onActivityPostStarted(activity: Activity) {
        val duration = System.currentTimeMillis() - eventsTime.poll(activity.hashCode())
        LogUtil.d(TAG_ACTIVITY, FMT_TIMEUSED.format(activity.javaClass.simpleName, duration))
    }

    override fun onActivityPreResumed(activity: Activity) {
        eventsTime.put(activity.hashCode(), System.currentTimeMillis())
    }

    override fun onActivityResumed(activity: Activity) {
        LogUtil.v(TAG_ACTIVITY, activity.javaClass.simpleName + ": onActivityResumed")
    }

    override fun onActivityPostResumed(activity: Activity) {
        val duration = System.currentTimeMillis() - eventsTime.poll(activity.hashCode())
        LogUtil.d(TAG_ACTIVITY, FMT_TIMEUSED.format(activity.javaClass.simpleName, duration))
    }

    override fun onActivityPrePaused(activity: Activity) {
        eventsTime.put(activity.hashCode(), System.currentTimeMillis())
    }

    override fun onActivityPaused(activity: Activity) {
        LogUtil.v(TAG_ACTIVITY, activity.javaClass.simpleName + ": onActivityPaused")
    }

    override fun onActivityPostPaused(activity: Activity) {
        val duration = System.currentTimeMillis() - eventsTime.poll(activity.hashCode())
        LogUtil.d(TAG_ACTIVITY, FMT_TIMEUSED.format(activity.javaClass.simpleName, duration))
    }

    override fun onActivityPreStopped(activity: Activity) {
        eventsTime.put(activity.hashCode(), System.currentTimeMillis())
    }

    override fun onActivityStopped(activity: Activity) {
        LogUtil.v(TAG_ACTIVITY, activity.javaClass.simpleName + ": onActivityStopped")
    }

    override fun onActivityPostStopped(activity: Activity) {
        val duration = System.currentTimeMillis() - eventsTime.poll(activity.hashCode())
        LogUtil.d(TAG_ACTIVITY, FMT_TIMEUSED.format(activity.javaClass.simpleName, duration))
    }

    override fun onActivityPreDestroyed(activity: Activity) {
        eventsTime.put(activity.hashCode(), System.currentTimeMillis())
    }

    override fun onActivityDestroyed(activity: Activity) {
        LogUtil.v(TAG_ACTIVITY, activity.javaClass.simpleName + ": onActivityDestroyed")
    }

    override fun onActivityPostDestroyed(activity: Activity) {
        val duration = System.currentTimeMillis() - eventsTime.poll(activity.hashCode())
        LogUtil.d(TAG_ACTIVITY, FMT_TIMEUSED.format(activity.javaClass.simpleName, duration))
    }

    override fun onActivityPreSaveInstanceState(activity: Activity, outState: Bundle) {
        super.onActivityPreSaveInstanceState(activity, outState)
    }

    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
    }

    override fun onActivityPostSaveInstanceState(activity: Activity, outState: Bundle) {
        super.onActivityPostSaveInstanceState(activity, outState)
    }

    /* ========== ========== ========== ========== ========== ========== ========== */

    override fun onFragmentPreAttached(
        fm: FragmentManager, fragment: Fragment, context: Context
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "????? ", ": onFragmentPreAttached"))
    }

    override fun onFragmentAttached(
        fm: FragmentManager, fragment: Fragment, context: Context
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "@@@@ ", ": onFragmentAttached"))
    }

    override fun onFragmentPreCreated(
        fm: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment) + ": onFragmentPreCreated")
    }

    override fun onFragmentCreated(
        fm: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "### ", ": onFragmentCreated"))
    }

    override fun onFragmentViewCreated(
        fm: FragmentManager, fragment: Fragment, view: View, savedInstanceState: Bundle?
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "++ ", ": onFragmentViewCreated"))
    }

    override fun onFragmentStarted(
        fm: FragmentManager, fragment: Fragment
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "== ", ": onFragmentStarted"))
    }

    override fun onFragmentResumed(
        fm: FragmentManager, fragment: Fragment
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "- ", ": onFragmentResumed"))
    }

    override fun onFragmentPaused(
        fm: FragmentManager, fragment: Fragment
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "- ", ": onFragmentPaused"))
    }

    override fun onFragmentStopped(
        fm: FragmentManager, fragment: Fragment
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "== ", ": onFragmentStopped"))
    }

    override fun onFragmentViewDestroyed(
        fm: FragmentManager, fragment: Fragment
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "++ ", ": onFragmentViewDestroyed"))
    }

    override fun onFragmentDestroyed(
        fm: FragmentManager, fragment: Fragment
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "#### ", ": onFragmentDestroyed"))
    }

    override fun onFragmentDetached(
        fm: FragmentManager, fragment: Fragment
    ) {
        LogUtil.v(TAG_FRAGMENT, chain(fragment, "@@@@@ ", ": onFragmentDetached"))
    }

    private fun chain(fragment: Fragment, prefix: String = "", suffix: String = ""): String {
        val activity = fragment.activity
        val array = if (activity == null) {
            arrayOf<Any>(*fragment.ancestors(true, true))
        } else {
            arrayOf<Any>(activity, *fragment.ancestors(true, true))
        }

        return array.joinToString(" -> ", prefix.padStart(8), suffix) {
            FMT_HASHNAME.format(it.javaClass.simpleName, it.hashCode())
        }
    }
}
